/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package JFreeChartExtensions;

import JFreeChartExtensions.chartextensions.AbstractChartPanelExtension;
import JFreeChartExtensions.Collections.DataRenderersCollection;
import JFreeChartExtensions.Collections.DatasetCollection;
import JFreeChartExtensions.containers.IChartPanelContainer;
import JFreeChartExtensions.events.ChartChangedEvent;
import JFreeChartExtensions.events.ChartEventPropertiesNames;
import JFreeChartExtensions.chartextensions.AbstractStorableChartPanelExtension;
import JFreeChartExtensions.exceptions.ExtendableChartPanelException;
import JFreeChartExtensions.exceptions.ExtensionLauncherException;
import JFreeChartExtensions.interfaces.IDataProvider;
import JFreeChartExtensions.interfaces.IPluginProvider;
import JFreeChartExtensions.listeners.ExtendedChartProgresListener;
import JFreeChartExtensions.listeners.IChartChangedListener;
import JFreeChartExtensions.listeners.IChartMouseActionsListener;
import JFreeChartExtensions.listeners.IChartOnPaintListener;
import JFreeChartExtensions.renderers.XYErrorRendererExtended;
import JFreeChartExtensions.storable.StorableChartFrame;
import JFreeChartExtensions.storable.StorableExtenstionObject;
import JFreeChartExtensions.storable.StorableRendererObject;
import JFreeChartExtensions.storable.StorableSeriesObject;
import JFreeChartExtensions.interfaces.Plugin;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import java.util.Locale;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.urls.StandardXYURLGenerator;
import org.jfree.chart.util.ResourceBundleWrapper;
import org.jfree.data.xy.XYDataset;

/**
 *
 * @author Nobody
 */
public class ExtendableChartPanel extends ChartPanel {

    //private static final Logger l = Logger.getLogger(Main.class);
    private DatasetCollection datasetCollection;
    private ExtensionManager extensionManager;
    private List<ExtendedChartProgresListener> chartProgresListeners = new LinkedList<ExtendedChartProgresListener>();
    private List<IChartOnPaintListener> chartOnPaintListeners = new LinkedList<IChartOnPaintListener>();
    private List<IChartMouseActionsListener> chartMouseActionsListeners = new LinkedList<IChartMouseActionsListener>();
    private List<IChartChangedListener> chartChangedListeners = new LinkedList<IChartChangedListener>();
    private Map<String, AbstractChartPanelExtension> abstractChartPanelExtensions = new HashMap<String, AbstractChartPanelExtension>();
    private IChartPanelContainer parentContainer;
    private IDataProvider dataProvider;

    public IDataProvider getDataProvider() {
        return dataProvider;
    }

    public void setDataProvider(IDataProvider dataProvider) {
        this.dataProvider = dataProvider;
    }

    public IChartPanelContainer getParentContainer() {
        return parentContainer;
    }

    static {
        localizationResources = ResourceBundleWrapper.getBundle(
                "org.jfree.chart.LocalizationBundle", Locale.ROOT);
    }

    public ExtendableChartPanel(JFreeChart smallChart, DatasetCollection collection) {
        super(smallChart);
        setLayout(null);
        removeSaveAsOption();
        datasetCollection = collection;

        DataRenderersCollection dataRenderersCollection = new DataRenderersCollection();
        dataRenderersCollection.setParentCollection(collection);
        dataRenderersCollection.setXyplot(smallChart.getXYPlot());
        datasetCollection.addRendererCollection(dataRenderersCollection);
    }

    public static JFreeChart createXYLineChart(String title,
            String xAxisLabel,
            String yAxisLabel,
            XYDataset dataset,
            PlotOrientation orientation,
            boolean legend,
            boolean tooltips,
            boolean urls) {

        if (orientation == null) {
            throw new IllegalArgumentException("Null 'orientation' argument.");
        }
        NumberAxis xAxis = new NumberAxis(xAxisLabel);
        xAxis.setAutoRangeIncludesZero(false);
        NumberAxis yAxis = new NumberAxis(yAxisLabel);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
        XYPlot plot = new ExtendedXYPlot(dataset, xAxis, yAxis, renderer);
        plot.setOrientation(orientation);
        if (tooltips) {
            renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
        }
        if (urls) {
            renderer.setURLGenerator(new StandardXYURLGenerator());
        }

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
                plot, legend);

        return chart;

    }

    public ExtendableChartPanel(String name, IChartPanelContainer parent) {
        super(null);
        this.parentContainer = parent;
        JFreeChart chart = createXYLineChart(
                "XY " + name,
                "X",
                "Y",
                null,
                PlotOrientation.VERTICAL,
                true,
                true,
                false);
        setChart(chart);
        setLayout(null);
        removeSaveAsOption();
        datasetCollection = new DatasetCollection();
        DataRenderersCollection masterRenderer = new DataRenderersCollection(datasetCollection);
        masterRenderer.setXyplot(chart.getXYPlot());
        datasetCollection.addRendererCollection(masterRenderer);
    }

    private void removeSaveAsOption() {
        getPopupMenu().remove(3);
    }

    public DatasetCollection getDatasetCollection() {
        return datasetCollection;
    }

    public void addChartProgressListener(ExtendedChartProgresListener listener) {
        chartProgresListeners.add(listener);
    }

    public void removeChartProgessListener(ExtendedChartProgresListener listener) {
        chartProgresListeners.remove(listener);
    }

    public void removeAllProgressListeners() {
        chartProgresListeners.clear();
    }

    public void addChartChangedListener(IChartChangedListener listener) {
        chartChangedListeners.add(listener);
    }

    public void removeChartChangedListener(IChartChangedListener listener) {
        chartChangedListeners.remove(listener);
    }

    public void removeAllChartChangedListeners() {
        chartChangedListeners.clear();
    }

    public void addChartOnPaintListener(IChartOnPaintListener listener) {
        chartOnPaintListeners.add(listener);
    }

    public void removeChartOnPaintListener(IChartOnPaintListener listener) {
        chartOnPaintListeners.remove(listener);
    }

    public void removeAllChartOnPaintListeners() {
        chartOnPaintListeners.clear();
    }

    public void addChartMouseActionListener(IChartMouseActionsListener listener) {
        chartMouseActionsListeners.add(listener);
    }

    public void removeChartMouseActionListener(IChartMouseActionsListener listener) {
        chartMouseActionsListeners.remove(listener);
    }

    public void removeAllChartMouseActionListener() {
        chartMouseActionsListeners.clear();
    }

    public void launchExtensions(IPluginProvider pluginProvider) throws ExtendableChartPanelException {
        ExtensionManager launcher = new ExtensionManager();
        List<Plugin> plugins = pluginProvider.getPlugins();
        for (Plugin plugin : plugins) {
            try {
                if (plugin.isAvailable()) {
                    AbstractChartPanelExtension extension = launcher.tryToCreateExtension(plugin, this);
                    addExtension(extension);
                }
            } catch (ExtensionLauncherException ex) {
                throw new ExtendableChartPanelException("exception during adding extensions", ex);
            }
        }
    }

    public void addExtension(AbstractChartPanelExtension abstractChartPanelExtension) throws ExtendableChartPanelException {
          try {
        abstractChartPanelExtension.applyExtension(this);
        abstractChartPanelExtensions.put(abstractChartPanelExtension.getName(), abstractChartPanelExtension);
        } catch (ExtensionLauncherException ex) {
        throw new ExtendableChartPanelException("exception in adding extenstion : " + abstractChartPanelExtension.getName(), ex);
        }
    }

    public void removeExtension(String name) {
        AbstractChartPanelExtension extension = abstractChartPanelExtensions.get(name);
        if (extension != null) {
            extension.removeExtension();
        }
        abstractChartPanelExtensions.remove(name);
        updateChart();

    }

    public AbstractChartPanelExtension getExtensionByName(String name) {
        return abstractChartPanelExtensions.get(name);
    }

    public Map<String, AbstractChartPanelExtension> getChartPanelExtensions() {
        return abstractChartPanelExtensions;
    }

    @Override
    public void chartProgress(ChartProgressEvent event) {
        if (event.getType() == ChartProgressEvent.DRAWING_FINISHED) {
            notifyAllProgressListeners();
        }
        super.chartProgress(event);
    }

    private void notifyAllProgressListeners() {
        for (ExtendedChartProgresListener listener : chartProgresListeners) {
            listener.chartChanged(this);
        }

    }

    /* private String constructNameOfSeries(ProgressInternalFrame frame, int xColumn, int yColumn) {
    return frame.getTitle() + (xColumn + ":" + yColumn);
    }*/
    public void updateChart() {
        getChart().fireChartChanged();
        updateUI();
    }

    public XYPlot getXYPlot() {
        return getChart().getXYPlot();
    }

    public boolean hasExtension(String string) {
        return abstractChartPanelExtensions.containsKey(string);
    }

    @Override
    public void mouseClicked(MouseEvent event) {
        for (IChartMouseActionsListener listener : chartMouseActionsListeners) {
            listener.mouseClicked(event);
        }
        super.mouseClicked(event);
        //  repaint();
    }

    @Override
    public void mousePressed(final MouseEvent event) {
        for (IChartMouseActionsListener listener : chartMouseActionsListeners) {
            listener.mousePressed(event);
        }
        super.mousePressed(event);
    }

    @Override
    public void mouseReleased(final MouseEvent event) {
        for (IChartMouseActionsListener listener : chartMouseActionsListeners) {
            listener.mouseReleased(event);
        }
        super.mouseReleased(event);
        //this.repaint();
    }

    @Override
    public void mouseDragged(final MouseEvent event) {
        for (IChartMouseActionsListener listener : chartMouseActionsListeners) {
            listener.mouseDragged(event);
        }
        super.mouseDragged(event);

    }

    @Override
    public void paint(final Graphics g) {
        super.paint(g);
        for (IChartOnPaintListener listener : chartOnPaintListeners) {
            listener.chartOnPaint(g);
        }
    }

    public void importStorableChart(StorableChartFrame chartFrame) {
        importSettings(chartFrame);
        importStorableSeries(chartFrame.getList());
    }

    private void importSettings(StorableChartFrame chartObject) {
        for (StorableRendererObject importedRenderer : chartObject.getStorableRenderersList()) {
            XYErrorRendererExtended renderer = new XYErrorRendererExtended();
            int i = importedRenderer.getSeriesIndex();
            renderer.setSeriesVisible(i, importedRenderer.isSeriesVisible());
            renderer.setSeriesShapesVisible(i, importedRenderer.isSeriesAsShapes());
            renderer.setSeriesLinesVisible(i, new Boolean(importedRenderer.isSeriesAsLines()));
            renderer.setSeriesXError(i, importedRenderer.isXErrorBarsVisible());
            renderer.setSeriesYError(i, importedRenderer.isYErrorBarsVisible());
            renderer.setSeriesPaint(i, importedRenderer.getSeriesColor());
            getXYPlot().setRenderer(importedRenderer.getDatasetIndex(), renderer);
        }
        this.updateChart();
        importExtensions(chartObject);
        datasetCollection.collectionStructureChanged();

    }

    private void importExtensions(StorableChartFrame chartObject) {
        for (StorableExtenstionObject extenstionObject : chartObject.getExtenstions()) {
            AbstractChartPanelExtension extension = getExtensionByName(extenstionObject.getName());
            if (extension instanceof AbstractStorableChartPanelExtension) {
                ((AbstractStorableChartPanelExtension) extension).loadStoredExtension(extenstionObject);
            }
        }
    }

    public ExtensionManager getExtensionManager() {
        return extensionManager;
    }

    private void importStorableSeries(List<StorableSeriesObject> list) {
        /* DatasetCollection datasetCollection = getDatasetCollection();
        for (StorableSeriesObject object : list) {
        XYIntervalSeries series = object.aSXYSeries();
        if (series instanceof XYRegressionSeries) {
        getDataSeriesCollections().get(object.getDataset()).addSeries(series);
        } else {

        XYReggresionSeriesCollection collection = new XYReggresionSeriesCollection(getDatasetCollection().size());
        collection.addSeries(series);
        }
        }*/
    }

    public boolean hasLogarithmicsXScale() {
        return getXYPlot().getDomainAxis() instanceof LogAxis;
    }

    public boolean hasLogarithmicsYScale() {
        XYPlot plot = getXYPlot();
        int size = plot.getRangeAxisCount();
        boolean isLogAxis = true;
        for (int i = 0; i < size; ++i) {
            if (!(plot.getRangeAxis(i) instanceof LogAxis)) {
                isLogAxis = false;
            }
        }
        return isLogAxis;
    }

    public void setDomainAxisAndNotifyListeners(int i, ValueAxis axis) {
        try {
            getXYPlot().setDomainAxis(i, axis);
            HashMap<ChartEventPropertiesNames, Object> propertiesMap = new HashMap<ChartEventPropertiesNames, Object>();
            propertiesMap.put(ChartEventPropertiesNames.SENDER, axis.clone());
            propertiesMap.put(ChartEventPropertiesNames.SENDER_TYPE, axis.getClass().toString());
            propertiesMap.put(ChartEventPropertiesNames.AXIS_INDEX, i);
            propertiesMap.put(ChartEventPropertiesNames.AXIS_TYPE_NAME, "Domain");

            notifyChartChangeListeners(propertiesMap);

        } catch (CloneNotSupportedException ex) {
            // l.error("error in cloning axis", ex);
        }
    }

    public void setRangeAxisAndNotifyListeners(int i, ValueAxis axis) {
        try {
            getXYPlot().setRangeAxis(i, axis);
            HashMap<ChartEventPropertiesNames, Object> propertiesMap = new HashMap<ChartEventPropertiesNames, Object>();
            if (axis != null) {
                propertiesMap.put(ChartEventPropertiesNames.SENDER, axis.clone());
                propertiesMap.put(ChartEventPropertiesNames.SENDER_TYPE, axis.getClass().toString());
            } else {
                propertiesMap.put(ChartEventPropertiesNames.SENDER_TYPE, ValueAxis.class.toString());
                propertiesMap.put(ChartEventPropertiesNames.SENDER,
                        null);
            }

            propertiesMap.put(ChartEventPropertiesNames.AXIS_INDEX, i);
            propertiesMap.put(ChartEventPropertiesNames.AXIS_TYPE_NAME, "Range");
            notifyChartChangeListeners(propertiesMap);

        } catch (CloneNotSupportedException ex) {
            //  l.error("error in cloning axis", ex);
        }
    }

    public void notifyRendererChangeListeners(int i) {
        XYErrorRendererExtended renderer = (XYErrorRendererExtended) getXYPlot().getRenderer(i);
        HashMap<ChartEventPropertiesNames, Object> propertiesMap = new HashMap<ChartEventPropertiesNames, Object>();
        propertiesMap.put(ChartEventPropertiesNames.SENDER, renderer);
        propertiesMap.put(ChartEventPropertiesNames.SENDER_TYPE, renderer.getClass());
        propertiesMap.put(ChartEventPropertiesNames.DATASET_INDEX, i);
        notifyChartChangeListeners(propertiesMap);
    }

    public void notifyChartChangeListeners(HashMap<ChartEventPropertiesNames, Object> map) {
        for (IChartChangedListener listener : chartChangedListeners) {
            listener.chartChanged(new ChartChangedEvent(this, map));
        }
    }

    public void mapDatasetToRangeAxisAndNotifyListeners(int seriesIndex, int axisNumber) {
        getChart().getXYPlot().mapDatasetToRangeAxis(seriesIndex, axisNumber);
        HashMap<ChartEventPropertiesNames, Object> propertiesMap = new HashMap<ChartEventPropertiesNames, Object>();
        propertiesMap.put(ChartEventPropertiesNames.SENDER, getXYPlot());
        propertiesMap.put(ChartEventPropertiesNames.SERIES_INDEX, seriesIndex);
        propertiesMap.put(ChartEventPropertiesNames.AXIS_INDEX, axisNumber);
        propertiesMap.put(ChartEventPropertiesNames.AXIS_TYPE_NAME, "Range");
        notifyChartChangeListeners(propertiesMap);
    }

    public DataRenderersCollection getRenderCollections() {
        return getDatasetCollection().getMasterRenderer();
    }
}
